package hasthi.tools.analyze;

import hasthi.common.HasthiException;
import hasthi.common.LogFile;
import hasthi.common.HasthiException.FaultCode;
import hasthi.test.PerfDataDocument;
import hasthi.test.PerfDataDocument.PerfData;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;

/**
 * This class collected the measured data sets, and send out a event whenever 
 * given number of data sets are collected. Furthermore it write each data set to a 
 * file. Also it can be find the summeries of the data set. 
 * @author hperera
 *
 */
public class DataSetBag {
    private static DecimalFormat df =  new DecimalFormat(".00");
//    public static DataSetBag instance = new DataSetBag("DafaultDataSet");
//    public static DataSetBag getInstance(){
//        return instance;
//    }
    private LogFile logfile = null;
    private boolean sendEvent = false;
    private boolean write2file = true;
    private int minimumSetCount = -1;
    
    private List<DataSet> dataset = new ArrayList<DataSet>();
    private String name;
    private boolean filterByTestName = true;
    
    public DataSetBag(String name) {
        this.name = name;
        logfile = LogFile.getLog(name);
    }
    public DataSetBag(String name,LogFile logfile) {
        this.name = name;
        this.logfile = logfile;
    }
    
    
    public void reset(){
        dataset.clear();
    }
    
    public void addData(PerfDataDocument perfDataDocument) throws HasthiException{
        PerfData perfData = perfDataDocument.getPerfData();
        if(perfData.getTestName().equals(name) || !filterByTestName){
            addData(new DataSet(perfDataDocument));
        }else{
            System.out.println("Event does not belong to this test"+ perfData.getTestName() + " != " + name + " Ignored");
        }
    }
    
    public void addData(DataSet dataSet) throws HasthiException{
        this.dataset.add(dataSet);
        if(minimumSetCount != -1 && minimumSetCount == this.dataset.size()){
            sendSummery();
        }
        String outStr = dataSet.toString();
        System.out.print(outStr);
        if(write2file){
            writeToFile(outStr);
        }
        System.out.println(this.dataset.size() + "/" + minimumSetCount + " completed");
    }
    public void addData(DataCollector collector) throws HasthiException{
        addData(new DataSet(collector));
    }
    
    public void sendSummery() throws HasthiException {
        System.out.println();
        long errorCount = 0;
        double min = Long.MAX_VALUE;
        double max = 0;
        double totMedian = 0;
        double totMean = 0;
        long reqSize = 0;
        for(DataSet d:this.dataset){
            if(d.min < min){
                min = d.min;
            }
            if(d.max > max){
                max = d.max;
            }
            totMedian = totMedian + d.median;
            errorCount = errorCount + d.failureCount;
            reqSize = reqSize + d.size;
            totMean = totMean + d.mean;
        }
        
        double avgMedian = ((double)totMedian)/this.dataset.size();
        double avgMean =  ((double)totMean)/this.dataset.size();
        
        if(write2file){
            StringBuffer buffer = new StringBuffer();
            buffer.append('[').append(name).append("]mean = ").append(avgMean).append(" median= ").append(avgMedian)
            .append(" min =").append(min).append(" max=").append(max).append(" faluires =").append(errorCount)
            .append("datasetCount=").append(this.dataset.size()).append("reading Count=").append(reqSize).append("\n");
            String lastOut = buffer.toString();
            System.out.print(lastOut);
            writeToFile(lastOut);
        }

        if(sendEvent){
            PerfDataDocument perfDataDocument = PerfDataDocument.Factory.newInstance();
            PerfData perfData = perfDataDocument.addNewPerfData();
            perfData.setTestName(name);
            perfData.setProducer(getLocalHostName());
            perfData.setMedian(avgMedian);
            perfData.setAvg(avgMean);
            perfData.setFaliureCount(errorCount);
            perfData.setMax(max);
            perfData.setMin(min);
            perfData.setRequestCount(reqSize);
            PerfAgent.getInstance().sendEvent(perfDataDocument);
        }
    }
    
    
    private void writeToFile(String str){
        logfile.append(str);
    }
    
    
    private static String localHostName;
    public static String getLocalHostName() throws HasthiException{
        try {
            if(localHostName == null){
                localHostName = InetAddress.getLocalHost().getHostName();
            }
            return localHostName;
        } catch (UnknownHostException e) {
            throw new HasthiException(e,FaultCode.LocalError);
        }
    }

    public String getName() {
        return name;
    }

    public void setFilterByTestName(boolean filterByTestName) {
        this.filterByTestName = filterByTestName;
    }


    public void setMinimumSetCount(int minimumSetCount) {
        this.minimumSetCount = minimumSetCount;
    }
    
    
    
    public static class DataSet{
        public DataSet(PerfDataDocument perfDataDocument){
            throw new UnsupportedOperationException();
        }
        
        public DataSet(DataCollector collector){
            DescriptiveStatistics data = collector.getData();
            mean = data.getMean();
            max = data.getMax();
            min = data.getMin();
            size = data.getN();
            median = data.getPercentile(50);
            cov = data.getStandardDeviation()/mean;
            failureCount = collector.getFaluireCount();
            
            
            databuf = new StringBuffer();
            
            databuf.append("mean =").append(df.format(mean)).append(" median=").append(df.format(median))
            .append(" cov =").append(df.format(cov)).append(" size =").append(size)
            .append(" min=").append(df.format(min)).append(" max =").append(df.format(max))
            .append(" faluireCount=").append(failureCount).append(' ');
            databuf.append("DATA[");
            for(double d:data.getValues()){
                databuf.append(d).append(' ');
            }
            databuf.append(']').append('\n');
        }
        public long size;
        public double min;
        public double max;
        public double cov;
        public double mean;
        public double median;
        public long failureCount;
        private StringBuffer databuf;
        
        @Override
        public String toString() {
            return databuf.toString();
        }
    }

    public void setSendEvent(boolean sendEvent) {
        this.sendEvent = sendEvent;
    }

    public void setWrite2file(boolean write2file) {
        this.write2file = write2file;
    }
}
